/******************************************************************************
 * Copyright 2022 The Airos Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *****************************************************************************/

#include "traffic_light_adapter_component.h"

#include <sys/time.h>
#include <vector>

#include "middleware/protocol/common/global_conf.h"

namespace os {
namespace v2x {
namespace protocol {

bool AIROS_COMPONENT_CLASS_NAME(TrafficLightAdapterComponent)::Init() {
  auto global_conf = GlobalConf::Instance();
  rscu_sn_ = global_conf->GetRscuSn();
  return true;
}

bool AIROS_COMPONENT_CLASS_NAME(TrafficLightAdapterComponent)::Proc(
    const std::shared_ptr<const os::v2x::TrafficLightServiceData>&
        service_data) {
  auto asn_pb = std::make_shared<v2xpb::asn::MessageFrame>();
  if (TrafficLightServicePb2AsnPb(service_data, asn_pb)) {
    Send("/v2x/message/generated", asn_pb);
  }
  if (send_step_control(2)) {
    auto cloud_pb = std::make_shared<os::v2x::device::CloudData>();
    if (TrafficLightServicePb2Cloud(service_data, cloud_pb)) {
      Send("/v2x/cloud/report/mqtt", cloud_pb);
    }
  }
  return true;
}

bool AIROS_COMPONENT_CLASS_NAME(TrafficLightAdapterComponent)::
    TrafficLightServicePb2Cloud(
        const std::shared_ptr<const os::v2x::TrafficLightServiceData>&
            service_data,
        std::shared_ptr<os::v2x::device::CloudData> cloud_pb) {
  if (!service_data || !cloud_pb) {
    return false;
  }
  auto mqtt_pb = cloud_pb->mutable_mqtt_data();
  mqtt_pb->set_topic(MQTT_TRAFFICLIGHT_TOPIC_PREFIX + rscu_sn_);
  std::string str_data;
  service_data->SerializePartialToString(&str_data);
  mqtt_pb->set_data(str_data);
  return true;
}

bool AIROS_COMPONENT_CLASS_NAME(TrafficLightAdapterComponent)::
    TrafficLightServicePb2AsnPb(
        const std::shared_ptr<const os::v2x::TrafficLightServiceData>&
            service_data,
        std::shared_ptr<v2xpb::asn::MessageFrame> asn_pb) {
  if (!service_data || !asn_pb) {
    return false;
  }

  const int pb_phase_cnt = service_data->traffic_light().phase().size();
  if (pb_phase_cnt == 0) {
    return false;
  }

  auto spat = asn_pb->mutable_spatframe();
  spat_count_ = (spat_count_ >= 127) ? 1 : (spat_count_ + 1);
  spat->set_message_count(spat_count_);
  spat->set_dsecond(get_mill_second_minute());

  auto inter = spat->add_intersections();
  inter->set_node_region(service_data->region_id());
  inter->set_node_id(service_data->cross_id());
  inter->set_moy(get_minute_year());
  inter->set_dsecond(get_mill_second_minute());

  int phase_index = 0;
  while (phase_index < pb_phase_cnt) {
    auto& light_phase = service_data->traffic_light().phase()[phase_index++];
    if (static_cast<uint32_t>(light_phase.light_id()) > MAX_VEHICLE_PHASE) {
      continue;
    }
    auto phase = inter->add_phases();
    phase->set_id(light_phase.light_id());

    os::v2x::LightState light_state = light_phase.light_status();
    bool is_light_unchanged = light_phase.light_unchanged();
    int32_t light_type = light_phase.light_type();

    std::vector<int32_t> period(3, 0);
    int32_t light_count_down = 0;
    if (is_light_unchanged != true) {
      for (int i = 0; (i < light_phase.step_info_list().size()) && (i < 3);
           i++) {
        period[i] = light_phase.step_info_list(i).duration();
      }
      // 正常有倒计时，异常情况：短暂无倒计时、非常亮情况下无倒计时、初始常亮学习阶段
      if (light_phase.has_count_down()) {
        light_count_down = light_phase.count_down();
      }
    } else {
      // 常亮灯时倒计时赋值99
      period[0] = 99;
      light_count_down = 99;
    }

    // 黄灯常亮
    if (is_light_unchanged && (light_state == os::v2x::LightState::YELLOW)) {
      light_state = os::v2x::LightState::FLASHING_YELLOW;
    }

    bool is_protected_green = false;
    if ((light_type == os::v2x::LightType::STRAIGHT_DIRECTION) ||
        (light_type == os::v2x::LightType::LEFT_DIRECTION) ||
        (light_type == os::v2x::LightType::RIGHT_DIRECTION) ||
        (light_type == os::v2x::LightType::TURN_DIRECTION)) {
      is_protected_green = true;
    }

    bool is_manual = false;
    if (light_count_down == 0 ||
        service_data->traffic_light().device_info().control_mode() ==
            os::v2x::ControlMode::LOCAL_MANUAL) {
      is_manual = true;
    }

    // 单相位无倒计时、倒计时为0、信号机手控时，SPAT不填倒计时，且每个相位只有一态
    if (is_manual) {
      phase->add_phase_state();
    } else {
      for (int i = 0; i < 3; i++) {
        phase->add_phase_state();
      }
    }

    switch (light_state) {
      case os::v2x::LightState::GREEN:  // 绿灯
      case os::v2x::LightState::FLASHING_GREEN:
        phase->mutable_phase_state(0)->set_color(
            is_protected_green ? v2xpb::asn::COLOR_PROTECTED_GREEN
                               : v2xpb::asn::COLOR_PERMISSIVE_GREEN);
        if (!is_manual) {
          phase->mutable_phase_state(0)->set_timing_start(0);
          phase->mutable_phase_state(0)->set_timing_end(light_count_down);
          phase->mutable_phase_state(0)->set_timing_duration(period[0]);

          phase->mutable_phase_state(1)->set_color(v2xpb::asn::COLOR_YELLOW);
          phase->mutable_phase_state(1)->set_timing_start(
              phase->mutable_phase_state(0)->timing_end());
          phase->mutable_phase_state(1)->set_timing_end(
              phase->mutable_phase_state(1)->timing_start() + period[1]);
          phase->mutable_phase_state(1)->set_timing_duration(period[1]);

          phase->mutable_phase_state(2)->set_color(v2xpb::asn::COLOR_RED);
          phase->mutable_phase_state(2)->set_timing_start(
              phase->mutable_phase_state(1)->timing_end());
          phase->mutable_phase_state(2)->set_timing_end(
              phase->mutable_phase_state(2)->timing_start() + period[2]);
          phase->mutable_phase_state(2)->set_timing_duration(period[2]);
        }
        break;
      case os::v2x::LightState::RED:  // 红灯
      case os::v2x::LightState::FLASHING_RED:
        phase->mutable_phase_state(0)->set_color(v2xpb::asn::COLOR_RED);
        if (!is_manual) {
          phase->mutable_phase_state(0)->set_timing_start(0);
          phase->mutable_phase_state(0)->set_timing_end(light_count_down);
          phase->mutable_phase_state(0)->set_timing_duration(period[0]);

          phase->mutable_phase_state(1)->set_color(
              is_protected_green ? v2xpb::asn::COLOR_PROTECTED_GREEN
                                 : v2xpb::asn::COLOR_PERMISSIVE_GREEN);
          phase->mutable_phase_state(1)->set_timing_start(
              phase->mutable_phase_state(0)->timing_end());
          phase->mutable_phase_state(1)->set_timing_end(
              phase->mutable_phase_state(1)->timing_start() + period[1]);
          phase->mutable_phase_state(1)->set_timing_duration(period[1]);

          phase->mutable_phase_state(2)->set_color(v2xpb::asn::COLOR_YELLOW);
          phase->mutable_phase_state(2)->set_timing_start(
              phase->mutable_phase_state(1)->timing_end());
          phase->mutable_phase_state(2)->set_timing_end(
              phase->mutable_phase_state(2)->timing_start() + period[2]);
          phase->mutable_phase_state(2)->set_timing_duration(period[2]);
        }
        break;
      case os::v2x::LightState::YELLOW:  // 黄灯
        phase->mutable_phase_state(0)->set_color(v2xpb::asn::COLOR_YELLOW);
        if (!is_manual) {
          phase->mutable_phase_state(0)->set_timing_start(0);
          phase->mutable_phase_state(0)->set_timing_end(light_count_down);
          phase->mutable_phase_state(0)->set_timing_duration(period[0]);

          phase->mutable_phase_state(1)->set_color(v2xpb::asn::COLOR_RED);
          phase->mutable_phase_state(1)->set_timing_start(
              phase->mutable_phase_state(0)->timing_end());
          phase->mutable_phase_state(1)->set_timing_end(
              phase->mutable_phase_state(1)->timing_start() + period[1]);
          phase->mutable_phase_state(1)->set_timing_duration(period[1]);

          phase->mutable_phase_state(2)->set_color(
              is_protected_green ? v2xpb::asn::COLOR_PROTECTED_GREEN
                                 : v2xpb::asn::COLOR_PERMISSIVE_GREEN);
          phase->mutable_phase_state(2)->set_timing_start(
              phase->mutable_phase_state(1)->timing_end());
          phase->mutable_phase_state(2)->set_timing_end(
              phase->mutable_phase_state(2)->timing_start() + period[2]);
          phase->mutable_phase_state(2)->set_timing_duration(period[2]);
        }
        break;
      case os::v2x::LightState::FLASHING_YELLOW:  // 黄闪
        phase->mutable_phase_state(0)->set_color(
            v2xpb::asn::COLOR_FLASHING_YELLOW);
        if (!is_manual) {
          phase->mutable_phase_state(0)->set_timing_start(0);
          phase->mutable_phase_state(0)->set_timing_end(light_count_down);
          phase->mutable_phase_state(0)->set_timing_duration(period[0]);

          phase->mutable_phase_state(1)->set_color(v2xpb::asn::COLOR_RED);
          phase->mutable_phase_state(1)->set_timing_start(
              phase->mutable_phase_state(0)->timing_end());
          phase->mutable_phase_state(1)->set_timing_end(
              phase->mutable_phase_state(1)->timing_start() + period[1]);
          phase->mutable_phase_state(1)->set_timing_duration(period[1]);

          phase->mutable_phase_state(2)->set_color(
              is_protected_green ? v2xpb::asn::COLOR_PROTECTED_GREEN
                                 : v2xpb::asn::COLOR_PERMISSIVE_GREEN);
          phase->mutable_phase_state(2)->set_timing_start(
              phase->mutable_phase_state(1)->timing_end());
          phase->mutable_phase_state(2)->set_timing_end(
              phase->mutable_phase_state(2)->timing_start() + period[2]);
          phase->mutable_phase_state(2)->set_timing_duration(period[2]);
        }
        break;
      default:
        return false;
    }
  }

  return true;
}

bool AIROS_COMPONENT_CLASS_NAME(
    TrafficLightAdapterComponent)::send_step_control(int step) {
  static int occurrences = 0;
  if (++occurrences > step) {
    occurrences -= step;
  }
  if (occurrences == 1) {
    return true;
  }
  return false;
}

int64_t AIROS_COMPONENT_CLASS_NAME(
    TrafficLightAdapterComponent)::get_minute_year() {
  struct tm* t = nullptr;
  time_t startTime = time(0);

  struct tm buf = {};
  localtime_r(&startTime, &buf);
  t = &buf;

  if (t == nullptr) {
    return -1;
  }
  return (t->tm_yday * 60 * 24 + t->tm_hour * 60 + t->tm_min);
}

int64_t AIROS_COMPONENT_CLASS_NAME(
    TrafficLightAdapterComponent)::get_mill_second_minute() {
  struct timeval tv;
  if (gettimeofday(&tv, NULL) != 0) {
    return -1;
  }
  struct tm* t = nullptr;
  time_t startTime = time(0);

  struct tm buf = {};
  localtime_r(&startTime, &buf);
  t = &buf;

  if (t == nullptr) {
    return -1;
  }
  return (t->tm_sec * 1000 + tv.tv_usec / 1000);
}

}  // namespace protocol
}  // namespace v2x
}  // namespace os
